/**
 * 
 */

/** Constants */
var WINDOW_WIDTH=400;
var WINDOW_HEIGHT=400;

var minX=-WINDOW_WIDTH/2;
var minY=-WINDOW_HEIGHT/2;
var maxX=-minX;
var maxY=-minY;
/** class Function */
function Function2D(points){
	
	this.getPoints= function(){
		var p=points.split(",");
		var fp=new Array();
		for(var i=0;i<p.length-1;i=i+2){
			var next=new Point(p[i],p[i+1]);
			fp.push(next);
		}
		
		return fp;
	};
}
/** class Point */
function Point(x,y){
	this.x=x;
	this.y=y;
}

function invoke(){
	var functionString=document.getElementById("function").value;
	var scaleX=document.getElementById("sx").value;
	var scaleY=document.getElementById("sy").value;
	FunctionProcessor(functionString,scaleX,scaleY);
}
/** Function Processor */
function FunctionProcessor(functionString,scaleX,scaleY){
	WINDOW_HEIGHT=$("canvas").height();
	WINDOW_WIDTH=$("canvas").width();
    minX=-WINDOW_WIDTH/2;
    minY=-WINDOW_HEIGHT/2;
    maxX=-minX;
    maxY=-minY;
	this.increment=0.1;
	this.scaleX=scaleX;
	this.scaleY=scaleY;
	
	
	this.getGraph= function(){
		var xValues=new Array();
		
		//form all x values
		for(var x=minX;x<maxX;x=x+this.increment){
			
			var sx=scaleX*x;
			xValues.push(sx);
		}
		
		//last point
		var sx=maxX*scaleX;
		xValues.push(sx);
		var xValueString=xValues.toString();
		//do an ajax call
		
		$.post(
			"rs/function/eval",
			{functionText:functionString,xArray:xValueString},
			//handler
			function(data){
				var f=new Function2D(data);			
				//check points
				var points=f.getPoints();
				var checkedPoints=new Array();
				for(var i=0;i<points.length;i++){
					if(!isNaN(points[i].y)&&isWithinBound(points[i])){
						checkedPoints.push(points[i]);
					}
					
				}
				var scaleY=getScaleY(checkedPoints);
				drawingWork(checkedPoints,scaleX, scaleY);
			}
		);
		
		
	};
	
	this.isWithinBound=function(point){
		return (parseFloat(point.x)>=parseFloat(minX)&&parseFloat(point.x)<=parseFloat(maxX)&&parseFloat(point.y)>=parseFloat(minY)&&parseFloat(point.y)<=parseFloat(maxY));
	};
	
	this.getMinY= function(points){
	
		if(points.length==0){
			return 0;
		}
		var y=points[0].y;
		for(var i=1;i<points.length;i++){
			if(parseFloat(y)>parseFloat(points[i].y)){
				y=points[i].y;
			}
		}
		
		return y;
	};
	
	this.getMaxY= function(points){
	
		if(points.length==0){
			return 0;
		}
		var y=points[0].y;
		for(var i=1;i<points.length;i++){
			if(parseFloat(y)<parseFloat(points[i].y)){
				y=points[i].y;
			}
		}
		return y;
	};
	
	this.disY=function(points){
		return this.getMaxY(points)-this.getMinY(points);
	};
	
	this.getScaleY=function(points){
		var disY=this.disY(points);
		var sy=1;
		if(disY!=0){
			sy=WINDOW_HEIGHT/disY;
		}
		var ay=Math.abs(sy)*this.scaleY;
		return ay;
	};
	
	this.getScaleX=function(){
		return this.scaleX;
	};
	
	//start drawing
	this.getGraph();
	
}
/** Util */
function Util(){
	this.toGraphPoint=function(p,scaleX,scaleY){
		var point=new Point();
		point.x=p.x/scaleX+WINDOW_WIDTH/2;
		point.y=-p.y*scaleY+WINDOW_HEIGHT/2;
		return point;
	};
	
	this.toMathPoint=function(p,scaleX,scaleY){
		var point=new Point();
		point.x=(p.x-WINDOW_WIDTH/2)*scaleX;
		point.y=(-p.y+WINDOW_HEIGHT/2)/scaleY;
		return point;
	};
}


// graphics


/**
 * use processing js
 */
var binding;

function drawingWork(points,scaleX, scaleY) {
	//detach existing
	if(binding){
		binding.exit();
	}

	// function
	var util= new Util(); 
	
	this.drawer = function(processing) {
		
		// do all drawing here
		processing.setup = function() {
			processing.size(WINDOW_WIDTH, WINDOW_HEIGHT);
			processing.strokeWeight(2);
			processing.smooth();
			processing.frameRate(15);
			processing.background(100);

		};

		processing.draw = function() {

			processing.background(100);
			drawAxis();
		
			processing.strokeWeight(1);
			for ( var i = 0; i < points.length - 1; i++) {
				var p1 = points[i];
				var p2 = points[i + 1];
				drawLine(p1, p2, scaleX, scaleY);

			}
			
			// draw mouse position
			var mouseP=new Point(processing.mouseX,processing.mouseY);
			var mathPoint=util.toMathPoint(mouseP, scaleX, scaleY);
			var x=mathPoint.x;
			var y=mathPoint.y;
			processing.text("(x="+x+","+"y="+y.toFixed(1)+")",processing.mouseX,processing.mouseY);
		};
		
		function drawLine(p1, p2,  scaleX,  scaleY) {

			var g1 = util.toGraphPoint(p1, scaleX, scaleY);
			var g2 = util.toGraphPoint(p2, scaleX, scaleY);
			processing.line(g1.x, g1.y, g2.x, g2.y);

		}
		function drawAxis(){
			processing.strokeWeight(1);
			// x axis
			drawArrow(new Point(minX, 0), new Point(
					maxX, 0));

			for (var x = minX; x <= maxX; x = x + 100) {
				var p = new Point(x, 0);
				drawNodeX(p, x * scaleX);
			}

			// y axis
			drawArrow(new Point(0, minY), new Point(0,
					maxY));

			for ( var y = minY; y <= maxY; y = y + 100) {
				var p = new Point(0, y);
				if ( y != 0) {
					drawNodeY(p, y / scaleY);
				}
			}

		}
		
		function drawNodeX( p,  num) {

			var g1 = util.toGraphPoint(new Point(p.x, p.y + 10),1,1);
			var g2 = util.toGraphPoint(new Point(p.x, p.y - 10),1,1);
			processing.line(g1.x, g1.y, g2.x, g2.y);
			var text = util.toGraphPoint(new Point(p.x + 5, p.y - 20),1,1);
			
			processing.text(num.toFixed(1), text.x, text.y);
		}

		function drawNodeY( p,  num) {

			var g1 = util.toGraphPoint(new Point(p.x - 10, p.y),1,1);
			var g2 = util.toGraphPoint(new Point(p.x + 10, p.y),1,1);
			processing.line(g1.x, g1.y, g2.x, g2.y);
			var text = util.toGraphPoint(new Point(p.x + 10, p.y + 10),1,1);
			
			processing.text(num.toFixed(1), text.x, text.y);
		}

		// draw an arrow
		function arrow( x1,  y1,  x2,  y2) {
			processing.line(x1, y1, x2, y2);
			processing.pushMatrix();
			processing.translate(x2, y2);
			var a = processing.atan2(x1 - x2, y2 - y1);
			 processing.rotate(a);
			 processing.line(0, 0, -10, -10);
			 processing.line(0, 0, 10, -10);
			 processing.popMatrix();
		}

		function drawArrow( p1,  p2) {
			var g1 = util.toGraphPoint(p1,1,1);
			var g2 = util.toGraphPoint(p2,1,1);
			arrow(g1.x, g1.y, g2.x, g2.y);
		}
		

		
	};

	// attach to canvas
	var canvas = document.getElementById("canvas");
	
	// attaching the drawing function to the canvas
	binding=new Processing(canvas, drawer);
	
}


	